%{
/* calc.h start */
typedef double (*func_t)(double);
/* 链表节点的数据类型 */
struct symrec
{
	char* name; /* 符号名称 */
	int type;/* 符号类型:VAR或FNCT */
	union
	{
		double var;/* VAR的值 */
		func_t fnctptr;/* FNCT的值 */
	} value;
	struct symrec* next;/* 指针域 */
};
typedef struct symrec symrec;
/* 符号表:`struct symrec`的链表 */
extern symrec* sym_table;
symrec* putsym(char const*,int);
symrec* getsym(char const*);
/* calc.h start */
#include <stdio.h>
#include <math.h>/* 数学函数sin,cos等 */
#include <string.h>
#include <ctype.h>
#include <malloc.h>
int yylex(void);
void yyerror(char const*);
%}

/* union声明了所有可能的类型清单，用来取代YYSTYPE，现在允许的类型是双精度和指向符号表目录指针 */
%union
{
	double val;/* 返回的数值 */
	symrec* tptr;/* 返回的符号指针 */
}
%token <val> NUM /* <val>为上面union中声明的类型，简单的双精度数值 */
%token <tptr> VAR FNCT /* 变量和函数 */
/* type用来声明非终结符，常在定义他们的规则中隐式定义，显示声明之后可以方便我们使用他的语义值类型 */
%type <val> exp

%right '='
%left '+' '-'
%left '*' '/'
%left EDG /* 负号 */
%right '^' /* 幂 */
%%
input:/* empty */
	| input line
;
line: '\n'
	| exp '\n' { printf("exp = %f\n",$1); }
	| error '\n' { yyerrok; }/* 错误处理 */
;
exp:NUM { $$ = $1; }
	| VAR { $$ = $1->value.var; }
	| VAR '=' exp{ $$ = $3; $1->value.var = $3; }
	| FNCT '(' exp ')' {$$ = (*($1->value.fnctptr))($3); }
	| FNCT '(' VAR ')' {$$ = (*($1->value.fnctptr))($3->value.var); }
	| exp '+' exp { $$ = $1 + $3; }
	| exp '-' exp { $$ = $1 - $3; }
	| exp '*' exp { $$ = $1 * $3; }
	| exp '/' exp { $$ = $1 / $3; }
	| '-' exp %prec NEG { $$ = -$2; }
	| exp '^' exp { $$ = pow($1,$3); }
	| '(' exp ')' { $$ = $2; }
;
%%
struct init
{
	char const* fname;
	double (*fnct)(double);
};
/* 函数隐射表 */
struct init const arith_fncts[] = 
{
	{"sin",sin},
	{"cos",cos},
	{"atan",atan},
	//"ln",ln,
	{"exp",exp},
	{"sqrt",sqrt},
	{0,0}
};
/* 符号表:`struct symrec`链表 */
symrec* sym_table = NULL;
/* 初始化符号表 */
void init_table(void)
{
	int i = 0;
	symrec* ptr;
	while(arith_fncts[i].fname != 0)
	{
		ptr = putsym(arith_fncts[i].fname,FNCT);
		ptr->value.fnctptr = arith_fncts[i].fnct;
		i++;
	}
}
int main(void)
{
	init_table();
	return yyparse();
}
void yyerror(char const* s)
{
	printf("%s\n",s);
}
symrec* putsym(char const* sym_name,int sym_type)
{
	symrec* ptr;
	ptr = (symrec*)malloc(sizeof(symrec));
	ptr->name = (char*)malloc(strlen(sym_name) + 1);
	strcpy(ptr->name,sym_name);
	ptr->type = sym_type;
	ptr->value.var = 0;
	ptr->next = (struct symrec*)sym_table;
	sym_table = ptr;
	return ptr;
}
symrec* getsym(char const* sym_name)
{
	symrec* ptr = sym_table;
	while(ptr != NULL)
	{
		if(strcmp(sym_name,ptr->name) == 0)
		{
			return ptr;
		}
		ptr = ptr->next;
	}
	return (symrec*)NULL;
}
int yylex(void)
{
	char c;
	while((c = getchar()) == ' ' || c == '\t');
	/* 数字开头，解析为数字 */
	if(isdigit(c))
	{
		ungetc(c,stdin);
		scanf("%lf",&yylval.val);
		return NUM;
	}
	if(isalpha(c))
	{
		symrec* s = NULL;
		static char* symbuf = NULL;
		static int length = 0;
		int i = 0;
		/* 初始化缓冲区 */
		if(length == 0)
		{
			length = 88;
			symbuf = (char*)malloc(length + 1);
		}
		do
		{
			/* 大小不够时，扩大缓冲区 */
			if(i == length)
			{
				length += 88;
				symbuf = realloc(symbuf,length + 1);
			}
			symbuf[i++] = c;
			c = getchar();
		}
		while(isalnum(c) > 0);
		ungetc(c,stdin);
		symbuf[i] = '\0';
		s = getsym(symbuf);
		if(s == NULL)
		{
			s = putsym(symbuf,VAR);
		}
		yylval.tptr = s;
		return s->type;
	}
	return c;
}
